# -*- coding: utf-8 -*-
"""
    酒店相关接口
"""

import datetime
import random
import json

from flask import Blueprint, request, g, jsonify, current_app, render_template, url_for
from mongoengine import Q

from app.models import Destination, HotelProductOrder
from app.constants import user_agents, DEVICE_TYPES, ORDER_REMIND_EMAILS, MTU_TYPE
from app.utils import api_ok, api_error, safe_int
from app.controllers import GDSClient
from app.decorators import validate_form, html5_handle
from app.forms.hotel import HotelDirectBookForm, HotelDateAPIForm, HotelOrderForm


gds_client = GDSClient()


def hotel_to_mtu_info(hotels):
    """
    将gds返回的信息变成mtu所需信息
    """
    # MTU返回信息和酒店信息名称不一样，需要更改
    rename_dict = {
        'cover': 'cover_image',
        'stars': 'star',
        'recommended_reason': 'recommendation',
        'fee': 'lowest_price',
        'name': 'name_cn',
        'name_en': 'name_en',
        'address': 'address_cn',
        'hotel_id': 'id',
        'description': 'location',
        'comment_score': 'comment_score',
        'region': 'city'
    }
    base_info = {
        'tel': None,
        'mtu_id': 0,
        'mtu_type': MTU_TYPE.HOTEL,
        'currency': u'¥',
        # 'images': [],
        # 'time_consuming_min': 0,
        # 'time_consuming_max': 0,
        'is_set_hotel': False,
        # 'opening_time': None,
        # 'arrival_type': None,
        # 'destination_id': None,
        # 'prefered': False,
        # 'distance': 0,
        # 'category_id': 10,
        # 'region': None,
    }

    items = []
    for hotel in hotels:
        item = base_info.copy()
        for k, v in rename_dict.items():
            item[k] = hotel.get(v)
        # location
        item['location'] = {
            'lat': hotel.get('latitude'),
            'lng': hotel.get('longitude'),
        }

        # small covers
        item['small_covers'] = [
            item['cover'],
        ]
        items.append(item)
    return items


@current_app.route('/gds/hotels/search/')
def search_hotel():
    """
    酒店列表
    """
    args = {}
    for k, v in request.args.items():
        if k not in ['pageSize', 'pageIndex']:
            args[k] = v

    page_size = safe_int(args.get('count'), 10)
    args['pageSize'] = page_size

    page_index = safe_int(args.get('start')) or 1
    args['pageIndex'] = page_index

    # 根据目的地搜索
    if not args.get('regionId'):
        error = None
        destination_id = request.args.get('destination_id')
        if not destination_id:
            error = api_error(40213)
        else:
            destination_query = Destination.objects.filter(pk=destination_id)
            if not destination_query:
                error = api_error(40213)
            else:
                regionname = destination_query[0].name
        if error:
            raise error
        args['place'] = regionname

    res_data = gds_client.hotel_list(**args)

    hotels = res_data.get('hotels', [])
    count = res_data.get('count', 0)

    items = hotel_to_mtu_info(hotels)

    if (page_index - 1) * page_size + len(items) < count:
        next_start = page_index + 1
    else:
        next_start = None

    return jsonify({
        'items': items,
        'next_start': next_start
    })


@current_app.route('/gds/hotel/hot_city/')
def fetch_hot_city():
    """
    热门城市查询接口
    """

    hot_city_list = gds_client.hot_city_list()
    return jsonify(hot_city_list)


@current_app.route('/gds/hotel/detail/')
def fetch_hot_detail():
    """
    酒店房型详细信息接口
    """

    args = {}
    for k, v in request.args.items():
        args[k] = v

    data = gds_client.hotel_detail(**args)

    if data:
        # data = ret['data']
        del data['lowest_price']

        hotel = data['hotel']
        hotel['images'] = hotel['images'][:5]
        del hotel['fee']
        # del hotel['region_id']
        # del hotel['policy']
        # del hotel['facilities']
        # del hotel['description_cn']
        del hotel['description_en']

        products = data['products']
        for p in products:
            del p['nightly_prices']
            del p['cancellation_desc']
            del p['taxes_and_fees']
            del p['arrive_start_time']
            del p['extra_person_fees']
            del p['facilities']
            del p['cancellation_type']
            del p['arrive_end_time']
            del p['max_person_num']
            del p['total_price']
        ret = {'code': 0, 'data': data}
    else:
        ret = {}
    return jsonify(ret)


@current_app.route('/hotel/product/<hotel_id>/')
@validate_form(form_class=HotelDirectBookForm)
@html5_handle()
def fetch_hotel_product_detail(hotel_id):
    """
    酒店房型详情(可以跳转至酒店预订页面, mode=3)
    """

    form = g.form
    checkin_date = form.checkin_date.data
    checkout_date = form.checkout_date.data
    rooms_count = form.rooms_count.data
    product_id = form.product_id.data
    rooms_json = json.dumps([{"numberOfAdults": 1, "childAges": ""} for n in range(rooms_count)])

    hotel_detail = gds_client.hotel_detail(iHotelId=hotel_id,
                                           checkInDate=checkin_date.strftime('%Y-%m-%d'),
                                           checkOutDate=checkout_date.strftime('%Y-%m-%d'),
                                           rooms=rooms_json)

    if not hotel_detail or not hotel_detail['products']:
        return u'无法获取该酒店的房型信息'

    # 从搜索到的床型中选出指定房型
    product = None
    for p in hotel_detail['products']:
        if p['id'] == product_id:
            product = p
            break
    if not product:
        return u'该酒店不存在此种房型'

    context = g.context
    context['hotel_id'] = hotel_id
    context['checkin_date'] = checkin_date.strftime('%Y-%m-%d')
    context['checkout_date'] = checkout_date.strftime('%Y-%m-%d')
    context['rooms_count'] = rooms_count
    context['product_id'] = product_id
    context['hotel'] = hotel_detail
    context['product'] = product
    context['book_url'] = url_for('hotel_direct_book',
                                  hotel_id=hotel_id,
                                  checkin_date=checkin_date.strftime('%Y-%m-%d'),
                                  checkout_date=checkout_date.strftime('%Y-%m-%d'),
                                  rooms_count=rooms_count,
                                  product_id=product_id)
    # 直接预订要带着刷新当前页时提供的 device_id
    if request.args.get('device_id'):
        context['book_url'] += '&device_id=%s' % request.args.get('device_id')

    return render_template('hotel/hotel_room.html', **context)


@current_app.route('/hotel/direct_book/<hotel_id>/', methods=['get', 'post'])
@html5_handle()
def hotel_direct_book(hotel_id):
    """
    直接预订（非行程下创建酒店订单）
    有2种页面请求来源:
        [2] 不提供房型ID(product_id), 是在酒店详情页点击"直接预定"进入
        [3] 提供房型ID(product_id)时, 是在酒店详情页点击“具体房型”进入
    """

    # 用户填写订单时需要有效标识:
    # [1] user_id
    # [2] device_id
    # [3] 匿名状态随机生成的 device_id

    @validate_form(form_class=HotelDirectBookForm)
    def hotel_direct_book_get():

        user = request.user if request.user.is_authenticated() else None
        user_id = user.id if user else None

        device_id = request.headers.get('HTTP_VACATION_DEVICE_ID', None) or request.args.get('device_id', None)
        if not user and not device_id:
            # 为支持用户在非登录状态并且不提供device_id情境下提交订单, 需要为匿名用户随机生成一个device_id
            device_id = 'ANONYMOUS_' + ''.join(random.sample('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', 30))

        # 根据页面来源区分不同的订单填写模式(mode)
        if 'product_id' in request.args:
            mode = 3
        else:
            mode = 2

        form = g.form
        checkin_date = form.checkin_date.data
        checkout_date = form.checkout_date.data
        rooms_count = form.rooms_count.data
        product_id = form.product_id.data

        context = g.context
        context['mode'] = mode
        context['user'] = user
        context['device_id'] = device_id
        context['plan_id'] = None
        context['hotel_id'] = hotel_id
        context['checkin_date'] = checkin_date.strftime('%Y-%m-%d')
        context['checkout_date'] = checkout_date.strftime('%Y-%m-%d')
        context['rooms_count'] = rooms_count

        rooms_json = json.dumps([{"numberOfAdults": 1, "childAges": ""} for n in range(rooms_count)])

        hotel_detail = gds_client.hotel_detail(iHotelId=hotel_id,
                                               checkInDate=checkin_date.strftime('%Y-%m-%d'),
                                               checkOutDate=checkout_date.strftime('%Y-%m-%d'),
                                               rooms=rooms_json)

        if hotel_detail and hotel_detail['products']:
            # 渲染时不使用全部数据, 只提供默认或已选的一种房型(product)
            products = hotel_detail['products']
            # 默认房型选择
            # [1] 默认选择第一个房型
            product = products[0]
            # [2] 如果提供了 product_id 且该product_id 在搜索出来的房型中，选择 product_id 指定的房型
            for p in products:
                if p['id'] == str(product_id):
                    product = p
                    break
            context['product'] = product
            context['hotel_name'] = hotel_detail['hotel']['name_cn']
            context['product_name'] = product['room_name']
            context['hotel_country'] = hotel_detail['hotel']['country']
            context['hotel_city'] = hotel_detail['hotel']['city']
            # 正常返回的房型信息，床型列表可能为空。
            # 提交订单时，床型为非必选，可为空。
            data_json = {
                # 酒店预订信息
                'user_id': user_id,
                'hotel_id': hotel_id,
                'hotel_name': hotel_detail['hotel']['name_cn'],
                'checkin_date': checkin_date.strftime('%Y-%m-%d'),
                'checkout_date': checkout_date.strftime('%Y-%m-%d'),
                'rooms_count': rooms_count,
                # 单个房型信息
                'product_id': product['id'],
                'product_name': product['room_name'],
                'current_allotment': product['current_allotment'],
                'total_price': product['total_price'],
                'average_price': product['average_price'],
                'nightly_prices': product['nightly_prices'],
                'taxes_and_fees': product['taxes_and_fees'],
                'extra_person_fees': product['extra_person_fees'],
                'cancellation_type': product['cancellation_type'],
                'cancellation_desc': product['cancellation_desc'],
                'bed_types': product['bed_types'],
                # 用户填写信息
                # 修改订单时需要根据原有订单赋初始值
                # 前端用 localstorage 方式存储(key: hotel_id)
                'customer_preferences': '',
                'invoice_title': '',
                'contact_email': '',
                'contact_mobile': '',
                'rooms': [{'firstName': '',
                           'lastName': '',
                           'adultNum': 1,
                           'childAges': '',
                           # 正常返回的房型信息，床型列表可能为空
                           'bedTypeId': product['bed_types'][0]['id'] if product['bed_types'] else ''
                           } for n in range(rooms_count)],
            }
        else:
            # 请求接口失败 or 请求接口成功但没有可订房型
            data_json = {
                # 酒店预订信息
                # 搜索房型失败时可以获取 hotel_name, 访问接口失败时无法获取 hotel_name
                'user_id': user_id,
                'hotel_id': hotel_id,
                'hotel_name': hotel_detail['hotel']['name_cn'] if hotel_detail else '',
                'checkin_date': checkin_date.strftime('%Y-%m-%d'),
                'checkout_date': checkout_date.strftime('%Y-%m-%d'),
                'rooms_count': rooms_count,
                # 单个房型信息
                'product_id': '',
                'product_name': '',
                'current_allotment': 0,
                'total_price': '',
                'average_price': '',
                'nightly_prices': [],
                'taxes_and_fees': '',
                'extra_person_fees': '',
                'cancellation_type': '',
                'cancellation_desc': '',
                'bed_types': [],
                # 用户填写信息
                'customer_preferences': '',
                'invoice_title': '',
                'contact_email': '',
                'contact_mobile': '',
                'rooms': [{'firstName': '',
                           'lastName': '',
                           'adultNum': 1,
                           'childAges': '',
                           'bedTypeId': '',
                           } for n in range(rooms_count)],
            }

        context['data'] = json.dumps(data_json)
        return render_template('hotel/hotel_book.html', **context)

    def hotel_direct_book_post():
        formdata = request.form if request.form else request.json
        form = HotelOrderForm(formdata=formdata)
        if not form.validate():
            return jsonify({'code': 499, 'message': form.errors.keys()[0]})

        identity = form._identity

        # 用户提交订单时必须保证存在有效标识: user_id or device_id or (random)device_id
        user = request.user if request.user.is_authenticated() else None
        user_id = user.id if user else None
        device_id = request.headers.get('HTTP_VACATION_DEVICE_ID', None) or formdata.get('device_id', None)
        if not user and not device_id:
            return jsonify({'code': 401, 'message': 'user or device must be identified'})

        # 如果用户通过 user_id 提交订单, 必须验证提交的 user_id 跟当前登录用户匹配
        if form.user_id.data and form.user_id.data != user_id:
            return jsonify({'code': 405, 'message': 'A user can only book hotel for himself(herself)'})

        # 一条 HotelProductOrder 记录中只存储其一: user 或 device_id
        # (登录用户提交订单, 不存 device_id)
        if user:
            device_id = None

        # 根据 POST 表单组装订单参数
        dict_order_create = {
            'user_id': identity,
            'hotelId': form.hotel_id.data,
            'productId': form.product_id.data,
            'checkInDate': form.checkin_date.data.strftime('%Y-%m-%d'),
            'checkOutDate': form.checkout_date.data.strftime('%Y-%m-%d'),
            'rooms': form.rooms.data,
            'contactMobile': form.contact_mobile.data,
            'contactEmail': form.contact_email.data,
        }
        if form.customer_preferences.data:
            dict_order_create.update({'customerPreferences': form.customer_preferences.data})
        if form.arrive_hotel_time.data:
            dict_order_create.update({'arriveHotelTime': form.arrive_hotel_time.data})
        if form.invoice_title.data:
            dict_order_create.update({'invoiceTitle': form.invoice_title.data})

        # 通过 gds API 提交订单
        order_create_ret = gds_client.order_create(**dict_order_create)
        # gds_log('HotelDirectBook', order_create_ret)
        if order_create_ret and order_create_ret['code'] == 0:
            # API 正常, 提交订单成功
            order_create = order_create_ret['data']
            # 订单信息存储: HotelProductOrder
            hotel_product_order = {
                'order_id': order_create['order_id'],
                'total_price': order_create['total_price'],
                'identity': identity,
                'hotel_id': form.hotel_id.data,
                'product_id': form.product_id.data,
                'hotel_name': form.hotel_name.data,
                'product_name': form.product_name.data,
                'checkin_date': form.checkin_date.data,
                'checkout_date': form.checkout_date.data,
                'rooms': form.rooms.data,
                'contact_mobile': form.contact_mobile.data,
                'contact_email': form.contact_email.data,
                'hotel_country': form.hotel_country.data,
                'hotel_city': form.hotel_city.data,
                'customer_preferences': form.customer_preferences.data,
                'invoice_title': form.invoice_title.data,
            }
            if user_id:
                hotel_product_order.update({'user_id': user_id})
            elif device_id:
                hotel_product_order.update({'device_id': device_id})
            # 存储订单信息到本地
            hotelproductorder = HotelProductOrder.objects.create(**hotel_product_order)
            # 支付流程中需要的 order_id 为 HotelProductOrder 的 id
            order_create_ret['data']['order_id'] = 'HOTEL_' + str(hotelproductorder.id)
            return jsonify(order_create_ret)
        elif order_create_ret and order_create_ret['code'] != 0:
            # API 正常, 提交订单失败
            return jsonify(order_create_ret)
        else:
            # API 异常
            return jsonify({'code': 500, 'message': u'API 异常无法提交订单'})

    if request.method == 'GET':
        return hotel_direct_book_get()
    else:
        return hotel_direct_book_post()


@current_app.route('/hotel/direct_book/<hotel_id>/date/')
@html5_handle()
def hotel_date_page(hotel_id):
    """
    酒店订单页日期选择
    """

    context = g.context
    type = request.args.get('type', '')
    if type not in ['checkin_date', 'checkout_date']:
        return 'type must be checkin_date or checkout_date'
    context['type'] = type
    context['checkin_date'] = request.args.get('checkin_date') or datetime.date.today().strftime('%Y-%m-%d')
    context['checkout_date'] = request.args.get('checkout_date') or (datetime.date.today() + datetime.timedelta(days=1)).strftime('%Y-%m-%d')
    context['hotel_id'] = hotel_id
    if request.args.get('plan_id'):
        context['back_url'] = url_for('hotel_plan_book', hotel_id=hotel_id)
    else:
        context['back_url'] = url_for('hotel_direct_book', hotel_id=hotel_id)
    if 'product_id' in request.args.keys():
        context['product_id'] = request.args.get('product_id')

    return render_template('hotel/hotel_calendar.html', **context)


@current_app.route('/hotel/book/<hotel_id>/date_api/')
@validate_form(HotelDateAPIForm)
def hotel_date_api(hotel_id):
    """
    给日历插件使用的 api 接口
    """

    form = g.form

    start_date = form._start_date
    stop_date = form._stop_date
    checkin_date = form.checkin_date.data
    # checkout_date = form.checkout_date.data

    list_date = []
    if form.type.data == 'checkin_date':
        while start_date <= stop_date:
            is_available = start_date >= datetime.date.today()
            list_date.append({'date': start_date.strftime('%Y-%m-%d'),
                              'start': start_date.strftime('%Y-%m-%d'),
                              'title': start_date.strftime('%Y-%m-%d') if not is_available else ''
                              })
            start_date += datetime.timedelta(days=1)

    elif form.type.data == 'checkout_date':
        while start_date <= stop_date:
            is_available = start_date >= datetime.date.today() and start_date > checkin_date
            list_date.append({'date': start_date.strftime('%Y-%m-%d'),
                              'start': start_date.strftime('%Y-%m-%d'),
                              'title': start_date.strftime('%Y-%m-%d') if not is_available else ''
                              })
            start_date += datetime.timedelta(days=1)

    return json.dumps(list_date)


@current_app.route('/hotel/book/search/<hotel_id>/')
@validate_form(HotelDirectBookForm)
@html5_handle()
def room_search(hotel_id):
    """
    用户变更房间数量时刷新页面需要的 Ajax 接口
    """
    form = g.form

    checkin_date = form.checkin_date.data
    checkout_date = form.checkout_date.data
    rooms_count = form.rooms_count.data if form.rooms_count.data else 1
    context = g.context
    context['hotel_id'] = hotel_id
    context['checkin_date'] = checkin_date.strftime('%Y-%m-%d')
    context['checkout_date'] = checkout_date.strftime('%Y-%m-%d')
    context['rooms_count'] = rooms_count
    rooms_json = json.dumps([{"numberOfAdults": 1, "childAges": ""} for n in range(rooms_count)])
    hotel_detail = gds_client.hotel_detail(iHotelId=hotel_id,
                                           checkInDate=checkin_date.strftime('%Y-%m-%d'),
                                           checkOutDate=checkout_date.strftime('%Y-%m-%d'),
                                           rooms=rooms_json)
    if hotel_detail and hotel_detail['products']:
        products = hotel_detail['products']
        product = products[0]
        # 搜索房型时如果提供当前页面已选的 product_id，并且搜索出的新房型列表中仍然包含此房型
        # 需要保持页面内的已选房型不变
        product_id = form.product_id.data
        if product_id:
            for p in products:
                if p['id'] == product_id:
                    product = p
                    break
        context['products'] = [product]
        context['product'] = product
        context['hotel_name'] = hotel_detail['hotel']['name_cn']
        context['product_name'] = product['room_name']
        data_json = {
            'user_id': request.user.id if request.user.is_authenticated() else None,
            'hotel_id': hotel_id,
            'hotel_name': hotel_detail['hotel']['name_cn'],
            'checkin_date': checkin_date.strftime('%Y-%m-%d'),
            'checkout_date': checkout_date.strftime('%Y-%m-%d'),
            'rooms_count': rooms_count,
            'product_id': product['id'],
            'product_name': product['room_name'],
            'current_allotment': product['current_allotment'],
            'total_price': product['total_price'],
            'average_price': product['average_price'],
            'nightly_prices': product['nightly_prices'],
            'taxes_and_fees': product['taxes_and_fees'],
            'extra_person_fees': product['extra_person_fees'],
            'cancellation_type': product['cancellation_type'],
            'cancellation_desc': product['cancellation_desc'],
            'bed_types': product['bed_types'],
            'customer_preferences': '',
            'invoice_title': '',
            'contact_email': '',
            'contact_mobile': '',
            'rooms': [{'firstName': '',
                       'lastName': '',
                       'adultNum': 1,
                       'childAges': '',
                       'bedTypeId': product['bed_types'][0]['id'] if product['bed_types'] else ''
                       } for n in range(rooms_count)],
        }
    else:
        data_json = {
            'user_id': request.user.id if request.user.is_authenticated() else None,
            'hotel_id': hotel_id,
            'hotel_name': hotel_detail['hotel']['name_cn'] if hotel_detail else '',
            'checkin_date': checkin_date.strftime('%Y-%m-%d'),
            'checkout_date': checkout_date.strftime('%Y-%m-%d'),
            'rooms_count': rooms_count,
            'product_id': '',
            'product_name': '',
            'current_allotment': 0,
            'total_price': '',
            'average_price': '',
            'nightly_prices': [],
            'taxes_and_fees': '',
            'extra_person_fees': '',
            'cancellation_type': '',
            'cancellation_desc': '',
            'bed_types': [],
            'customer_preferences': '',
            'invoice_title': '',
            'contact_email': '',
            'contact_mobile': '',
            'rooms': [{
                          'firstName': '',
                          'lastName': '',
                          'bedTypeId': '',
                          'adultNum': 1,
                          'childAges': ''
                      } for n in range(rooms_count)],
        }
    return jsonify(data_json)

